home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 May: Tool Chest / Dev.CD May 97 TC.toast / Sample Code / Overview / CPlusTESample / Application.cp < prev    next >
Encoding:
Text File  |  1994-11-18  |  13.3 KB  |  595 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------------------
  2.  
  3.     Program:    CPlusTESample 2.0
  4.     File:        Application.cp
  5.     Uses:       Application.h
  6.                 Document.h
  7.  
  8.     by Andrew Shebanow
  9.     of Apple Macintosh Developer Technical Support
  10.  
  11.     Copyright © 1989-1990 Apple Computer, Inc.
  12.     All rights reserved.
  13.  
  14. ------------------------------------------------------------------------------------------*/
  15.  
  16. // Mac Includes
  17.  
  18. #define OBSOLETE
  19.     // for ClrAppFiles(), CountAppFiles(), GetAppFiles() et. all.
  20.     
  21. #ifndef __TYPES__
  22. #include <Types.h>
  23. #endif
  24. #ifndef __QUICKDRAW__
  25. #include <QuickDraw.h>
  26. #endif
  27. #ifndef __FONTS__
  28. #include <Fonts.h>
  29. #endif
  30. #ifndef __EVENTS__
  31. #include <Events.h>
  32. #endif
  33. #ifndef __CONTROLS__
  34. #include <Controls.h>
  35. #endif
  36. #ifndef __WINDOWS__
  37. #include <Windows.h>
  38. #endif
  39. #ifndef __RESOURCES__
  40. #include <Resources.h>
  41. #endif
  42. #ifndef __MENUS__
  43. #include <Menus.h>
  44. #endif
  45. #ifndef __TEXTEDIT__
  46. #include <TextEdit.h>
  47. #endif
  48. #ifndef __DIALOGS__
  49. #include <Dialogs.h>
  50. #endif
  51. #ifndef __MENUS__
  52. #include <Menus.h>
  53. #endif
  54. #ifndef __DEVICES__
  55. #include <Devices.h>
  56. #endif
  57. #ifndef __EVENTS__
  58. #include <Events.h>
  59. #endif
  60. #ifndef __SCRAP__
  61. #include <Scrap.h>
  62. #endif
  63. #ifndef __TOOLUTILS__
  64. #include <ToolUtils.h>
  65. #endif
  66. #ifndef __MEMORY__
  67. #include <Memory.h>
  68. #endif
  69. #ifndef __SEGLOAD__
  70. #include <SegLoad.h>
  71. #endif
  72. #ifndef __FILES__
  73. #include <Files.h>
  74. #endif
  75. #ifndef __OSUTILS__
  76. #include <OSUtils.h>
  77. #endif
  78. #ifndef __TRAPS__
  79. #include <Traps.h>
  80. #endif
  81.  
  82. #ifndef __APPLICATION__
  83. #include "Application.h"
  84. #endif
  85.  
  86. #ifndef __DOCUMENT__
  87. #include "Document.h"
  88. #endif
  89.  
  90. // OSEvent is the event number of the suspend/resume and mouse-moved events sent
  91. // by MultiFinder. Once we determine that an event is an osEvent, we look at the
  92. // high byte of the message sent to determine which kind it is. To differentiate
  93. // suspend and resume events we check the resumeMask bit.
  94. const short kOsEvent = app4Evt;                // event used by MultiFinder
  95. const short kSuspendResumeMessage = 0x01;    // high byte of suspend/resume event message
  96. const short kClipConvertMask = 0x02;        // bit of message field clip conversion
  97. const short kResumeMask = 0x01;                // bit of message field for resume vs. suspend
  98. const short kMouseMovedMessage = 0xFA;        // high byte of mouse-moved event message
  99.  
  100. extern "C" {
  101.     // from MPW standard library
  102.     void _DataInit();                // sets up A5 globals
  103. };
  104.  
  105. // useful state checking Boolean
  106. Boolean gHaveColorQD;
  107.  
  108. // just as "normal" global variables that are declared extern in
  109. // header files must still be declared somewhere in order to reserve
  110. // space, static class variables must also be declared outside of
  111. // the class definition. The syntax is confusing, but then, thats
  112. // what makes C++ so ***interesting***.
  113. OSType TApplication::fCreator;
  114.  
  115. TApplication::TApplication(OSType creator)
  116. {
  117.     SysEnvRec envRec;
  118.     long stkNeeded;
  119.     long heapSize;
  120.  
  121.     // initialize Mac Toolbox components
  122.     InitGraf((Ptr) &qd.thePort);
  123.     InitFonts();
  124.     InitWindows();
  125.     InitMenus();
  126.     TEInit();
  127.     InitDialogs(NULL);
  128.     InitCursor();
  129.  
  130.     // Unload data segment: note that _DataInit must not be in Main!
  131.     UnloadSeg((ProcPtr) _DataInit);
  132.  
  133.     // ignore the error returned from SysEnvirons; even if an error occurred,
  134.     // the SysEnvirons glue will fill in the SysEnvRec
  135.     (void) SysEnvirons(curSysEnvVers, &envRec);
  136.  
  137.     // Are we running on a 128K ROM machine or better???
  138.     if (envRec.machineType < 0)
  139.       BigBadError(kErrStrings,eWrongMachine);        // if not, alert & quit
  140.  
  141.     gHaveColorQD = envRec.hasColorQD;
  142.  
  143.     // if we need more stack space, get it now
  144.     stkNeeded = StackNeeded();
  145.     if (stkNeeded > StackSpace())
  146.       {
  147.         // new address is heap size + current stack - needed stack
  148.         SetApplLimit((Ptr) ((long) GetApplLimit() - stkNeeded + StackSpace()));
  149.       }
  150.  
  151.     // Check for minimum heap size
  152.     heapSize = (long) GetApplLimit() - (long) ApplicationZone();
  153.     if (heapSize < HeapNeeded())
  154.       BigBadError(kErrStrings,eSmallSize);
  155.  
  156.     // expand the heap so new code segments load at the top
  157.     MaxApplZone();
  158.  
  159.     // allocate an empty document list
  160.     fDocList = new TDocumentList;
  161.  
  162.     // check to see if WaitNextEvent is implemented
  163.     fHaveWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
  164.  
  165.     // initialize our class variables
  166.     fCurDoc = nil;
  167.     fDone = false;
  168.     fInBackground = false;
  169.     fMouseRgn = nil;
  170.     fWhichWindow = nil;
  171.     fCreator = creator;
  172. }
  173.  
  174. void TApplication::EventLoop()
  175. {
  176.     int gotEvent;
  177.     EventRecord tEvt;
  178.  
  179.     SetUp();                    // call setup routine
  180.     DoIdle();                    // do idle once
  181.  
  182.     while (!fDone)
  183.       {
  184.         // always set up fWhichWindow before doing anything
  185.         fWhichWindow = FrontWindow();
  186.         if (fWhichWindow != nil)
  187.           {
  188.             // see if window belongs to a document
  189.             fCurDoc = fDocList->FindDoc(fWhichWindow);
  190.             // make sure we always draw into correct window
  191.             SetPort(fWhichWindow);
  192.           }
  193.         else fCurDoc = nil;
  194.  
  195.         DoIdle();            // call idle time handler
  196.  
  197.         if (fHaveWaitNextEvent)
  198.           gotEvent = WaitNextEvent(everyEvent, &tEvt, SleepVal(), fMouseRgn);
  199.         else
  200.           {
  201.             SystemTask();
  202.             gotEvent = GetNextEvent(everyEvent, &tEvt);
  203.           }
  204.         fTheEvent = tEvt;
  205.  
  206.         // if we got a real event, process it
  207.         if (gotEvent)
  208.           ProcessEvent();
  209.  
  210.         AdjustCursor();
  211.       }
  212. }
  213.  
  214. void TApplication::ProcessEvent()
  215. {
  216.     // make sure alert is loaded in memory BEFORE we do any event
  217.     // processing. This is necessary since the alert for the case
  218.     // when we need to display an out of memory alert.
  219.     // CouldAlert(rUserAlert);
  220.     TRY
  221.       {
  222.         AdjustCursor();
  223.         switch (fTheEvent.what)
  224.           {
  225.             case mouseDown:
  226.                 DoMouseDown();
  227.                 break;
  228.             case mouseUp:
  229.                 DoMouseUp();
  230.                 break;
  231.             case keyDown:
  232.             case autoKey:
  233.                 DoKeyDown();
  234.                 break;
  235.             case updateEvt:
  236.                 DoUpdateEvt();
  237.                 break;
  238.             case diskEvt:
  239.                 DoDiskEvt();
  240.                 break;
  241.             case activateEvt:
  242.                 DoActivateEvt();
  243.                 break;
  244.             case kOsEvent:
  245.                 DoOSEvent();
  246.                 break;
  247.             default:
  248.                 break;
  249.           }
  250.       }
  251.     RECOVER
  252.       {
  253.         AlertUser((short) gFailMessage, gFailError);
  254.         // don't let error bubble up any farther
  255.         goto doneEvent;
  256.       }
  257.     ENDTRY
  258.  
  259. doneEvent:
  260.     return;
  261. }
  262.  
  263. void TApplication::DoKeyDown()
  264. {
  265.     char key;
  266.     long mResult;
  267.  
  268.     key = (char) (fTheEvent.message & charCodeMask);
  269.     if ((fTheEvent.modifiers & cmdKey) && (fTheEvent.what == keyDown))
  270.       {
  271.         // only do command keys if we are not autokeying
  272.         AdjustMenus();                    // make sure menus are up to date
  273.         mResult = MenuKey(key);
  274.         if (mResult != 0)                // if it wasn't a menu key, pass it through
  275.           {
  276.             DoMenuCommand(HiWord(mResult), LoWord(mResult));
  277.             return;
  278.           }
  279.       }
  280.     if (fCurDoc != nil)
  281.       {
  282.         EventRecord tEvt;
  283.  
  284.         // we copy event record so that we don't pass reference to object field
  285.         tEvt = fTheEvent;
  286.         fCurDoc->DoKeyDown(&tEvt);
  287.       }
  288. }
  289.  
  290. void TApplication::DoActivateEvt()
  291. {
  292.     // event record contains window ptr
  293.     fWhichWindow = (WindowPtr) fTheEvent.message;
  294.     // see if window belongs to a document
  295.     fCurDoc = fDocList->FindDoc(fWhichWindow);
  296.     SetPort(fWhichWindow);
  297.  
  298.     if (fCurDoc != nil)
  299.       fCurDoc->DoActivate((fTheEvent.modifiers & activeFlag) != 0);
  300. }
  301.  
  302. void TApplication::DoUpdateEvt()
  303. {
  304.     // event record contains window ptr
  305.     fWhichWindow = (WindowPtr) fTheEvent.message;
  306.     // see if window belongs to a document
  307.     fCurDoc = fDocList->FindDoc(fWhichWindow);
  308.     SetPort(fWhichWindow);
  309.  
  310.     if (fCurDoc != nil)
  311.       fCurDoc->DoUpdate();
  312. }
  313.  
  314. // NOTE: we use an anonymous parameter here so that the compiler
  315. // doesn't warn us about it being unused. Since we give it a name
  316. // in the class definition, we still know what its used for.
  317. void TApplication::DoSuspend(Boolean)
  318. {
  319.     if (fCurDoc != nil)
  320.       fCurDoc->DoActivate(!fInBackground);
  321. }
  322.  
  323. void TApplication::DoResume(Boolean)
  324. {
  325.     if (fCurDoc != nil)
  326.       fCurDoc->DoActivate(!fInBackground);
  327. }
  328.  
  329. void TApplication::DoOSEvent()
  330. {
  331.     Boolean doConvert;
  332.     unsigned char evType;
  333.  
  334.     // is it a multifinder event?
  335.     evType = (unsigned char) (fTheEvent.message >> 24) & 0x00ff;
  336.     switch (evType) {     // high byte of message is type of event
  337.         case kMouseMovedMessage:
  338.             DoIdle();                    // mouse-moved is also an idle event
  339.             break;
  340.         case kSuspendResumeMessage:
  341.             doConvert = (fTheEvent.message & kClipConvertMask) != 0;
  342.             fInBackground = (fTheEvent.message & kResumeMask) == 0;
  343.             if (fInBackground)
  344.               DoSuspend(doConvert);
  345.             else DoResume(doConvert);
  346.             break;
  347.     }
  348. }
  349.  
  350. void TApplication::DoMouseDown()
  351. {
  352.     long mResult;
  353.     short partCode;
  354.     WindowPtr tWind;
  355.     EventRecord tEvt;
  356.  
  357.     // gotta watch those object field dereferences
  358.     partCode = FindWindow(fTheEvent.where, &tWind);
  359.     fWhichWindow = tWind;
  360.     tEvt = fTheEvent;
  361.     switch (partCode)
  362.       {
  363.         case inSysWindow:
  364.             DoMouseInSysWindow();
  365.             break;
  366.         case inMenuBar:
  367.             AdjustMenus();
  368.             mResult = MenuSelect(tEvt.where);
  369.             if (mResult != 0)
  370.               DoMenuCommand(HiWord(mResult),LoWord(mResult));
  371.             break;
  372.         case inGoAway:
  373.             DoGoAway();
  374.             break;
  375.         case inDrag:
  376.             DoDrag();
  377.             break;
  378.         case inGrow:
  379.             if (fCurDoc != nil)
  380.               fCurDoc->DoGrow(&tEvt);
  381.             break;
  382.         case inZoomIn:
  383.         case inZoomOut:
  384.             if ((TrackBox(fWhichWindow, tEvt.where, partCode)) &&
  385.                 (fCurDoc != nil))
  386.               fCurDoc->DoZoom(partCode);
  387.             break;
  388.         case inContent:
  389.             // If window is not in front, make it so
  390.             if ( fWhichWindow != FrontWindow() )
  391.               SelectWindow(fWhichWindow);
  392.             else if (fCurDoc != nil)
  393.               fCurDoc->DoContent(&tEvt);
  394.             break;
  395.       }
  396. }
  397.  
  398. void TApplication::DoDrag()
  399. {
  400.     DragWindow(fWhichWindow, fTheEvent.where, &qd.screenBits.bounds);
  401. }
  402.  
  403. void TApplication::DoGoAway()
  404. {
  405.     if (TrackGoAway(fWhichWindow, fTheEvent.where))
  406.       {
  407.         if (fCurDoc != nil)
  408.           {
  409.             if (fCurDoc->DoClose(true, yesResult, false) != cancelResult)
  410.               {
  411.                 fDocList->RemoveDoc(fCurDoc);
  412.                 delete fCurDoc;
  413.               }
  414.           }
  415.         else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
  416.         // make sure our current document/window references are valid
  417.         fWhichWindow = FrontWindow();
  418.         if (fWhichWindow != nil)
  419.           {
  420.             fCurDoc = fDocList->FindDoc(fWhichWindow);
  421.             SetPort(fWhichWindow);
  422.           }
  423.         else fCurDoc = nil;
  424.       }
  425. }
  426.  
  427. void TApplication::ProcessArgs()
  428. {
  429.     short message, numFiles, curFile;
  430.     AppFile fileInfo;
  431.  
  432.     /* count the files */
  433.     CountAppFiles(&message,&numFiles);
  434.     if (numFiles == 0)
  435.       {
  436.         // create a single empty document
  437.         DoNew();
  438.         return;
  439.       }
  440.     for (curFile = 1; curFile <= numFiles; curFile++)
  441.       {
  442.         /* get file info */
  443.         GetAppFiles(curFile,&fileInfo);
  444.         /* open/print the file */
  445.         if (message != appPrint)
  446.           {
  447.             TRY
  448.               {
  449.                 OpenADoc(fileInfo.vRefNum,0,fileInfo.fName,fileInfo.fType);
  450.               }
  451.             RECOVER
  452.               {
  453.                 goto processNextFile;
  454.               }
  455.             ENDTRY
  456.           }
  457. processNextFile:
  458.         /* clear finder arg for this file */
  459.         ClrAppFiles(curFile);
  460.       }
  461. }
  462.  
  463. void TApplication::DoQuit(Boolean askUser, YNCResult defaultResult)
  464. {
  465.     while (true)
  466.       {
  467.         fWhichWindow = FrontWindow();
  468.         if (fWhichWindow == nil)
  469.           break;
  470.         fCurDoc = fDocList->FindDoc(fWhichWindow);
  471.         if (fCurDoc != nil)
  472.           {
  473.             // if the user cancels the quit
  474.             if (fCurDoc->DoClose(askUser, defaultResult, true) == cancelResult)
  475.               return;
  476.             else
  477.               {
  478.                 fDocList->RemoveDoc(fCurDoc);
  479.                 delete fCurDoc;
  480.               }
  481.           }
  482.         else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
  483.         // make sure we aren't in an infinite loop. This could occur
  484.         // if the CloseDeskAcc was failing for some reason.
  485.         if (FrontWindow() == fWhichWindow)
  486.           {
  487.             // send the window to the back of the list.
  488.             // if the FrontWindow is still the same, we will exit
  489.             // the loop. Otherwise, we let the loop keep running so
  490.             // that we have a chance to close our other windows
  491.             // cleanly
  492.             SendBehind(fWhichWindow, nil);
  493.             if (FrontWindow() == fWhichWindow)
  494.               break;
  495.           }
  496.       }
  497.     fDone = true;
  498.     fWhichWindow = nil;
  499.     fCurDoc = nil;
  500. }
  501.  
  502. Boolean TApplication::TrapAvailable(short tNumber,TrapType tType)
  503. {
  504.     // Check and see if the trap exists. On 64K ROM machines, tType will be ignored.
  505.     return NGetTrapAddress(tNumber, tType) != NGetTrapAddress(_Unimplemented, ToolTrap);
  506. }
  507.  
  508. void TApplication::WDToDirID(short wdRefNum, short& vRefNum, long& dirID)
  509. {
  510.     const short kRootDirID = 2;
  511.     long junk;
  512.  
  513.     OSErr err = GetWDInfo(wdRefNum,&vRefNum,&dirID,&junk);
  514.     if (err != noErr)
  515.       {
  516.         vRefNum = wdRefNum;        // if GetVol doesn't return valid vRefNum/dirID pair,
  517.         dirID = kRootDirID;        // use wdRefNum as a vRefNum and use root for dirID
  518.       }
  519. }
  520.  
  521. void AlertUser(short errResID, short errCode)
  522. {
  523.     Str255 messageStr;
  524.  
  525.     // if we have a hilited menu, turn it off before displaying alert
  526.     HiliteMenu(0);
  527.  
  528.     if (errResID != 0)
  529.       {
  530.         GetIndString(messageStr, errResID, errCode);
  531.         ParamText(messageStr, "\p", "\p", "\p");
  532.       }
  533.     else
  534.       {
  535.         // we need to lookup the error in our table
  536.         LookupErrorString(errCode,kSysErrStrings,messageStr);
  537.         ParamText(messageStr, "\p", "\p", "\p");
  538.       }
  539.     SetCursor(&qd.arrow);
  540.     (void) Alert(rUserAlert, (ModalFilterProcPtr) nil);
  541. }
  542.  
  543. void BigBadError(short errResID, short errCode)
  544. {
  545.     AlertUser(errResID,errCode);
  546.     ExitToShell();
  547. }
  548.  
  549. Boolean LookupErrorString(short value, short resID, StringPtr str)
  550. {
  551.     struct ErrRecord {
  552.         short lowErr;
  553.         short highErr;
  554.         short index;
  555.     };
  556.     typedef struct ErrRecord* ErrRecordPtr;
  557.  
  558.     Handle            table;
  559.     ErrRecordPtr    pEntry;
  560.     unsigned long    tableOffset;
  561.     long            lenTab;
  562.     int                strID;
  563.  
  564.     // start with an empty string
  565.     str[0] = 0;
  566.  
  567.     table = GetResource('errs', resID);
  568.     if (!table)
  569.       {
  570.         lenTab = (long) (GetHandleSize((Handle) table) / sizeof(ErrRecord));
  571.  
  572.         strID = 0;
  573.         tableOffset = 0;
  574.  
  575.         for (long i = 1; i <= lenTab; i++)
  576.           {
  577.             pEntry = (ErrRecordPtr) ((unsigned long) *table) + tableOffset;
  578.  
  579.             if (pEntry->lowErr == 0)
  580.               strID = pEntry->index;
  581.             else if ((pEntry->lowErr <= value) && (value <= pEntry->highErr))
  582.               {
  583.                 if (pEntry->index > 0)
  584.                   GetIndString(str, strID, pEntry->index);
  585.                 return true;
  586.               }
  587.  
  588.             tableOffset += sizeof(ErrRecord);
  589.           }
  590.       }
  591.     return false;
  592. }
  593.  
  594. // That's all, folks...
  595.